#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>		//内部包含了socket.h
#include <pthread.h>
#include "ThreadPool.h"

// 信息结构体
typedef struct SockInfo
{
	struct sockaddr_in addr;
	int fd;
}SockInfo;

// 包装好线程池信息用来线程之间的通讯
typedef struct PoolInfo {
	ThreadPool* p;
	int fd;
}PoolInfo;

void working(void* arg);
void accept_connect(void* arg);

int main()
{
	//第一步：创建监听的套接字
	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if (fd == -1)
	{
		perror("socket");
		return -1;
	}

	//第二步：绑定本地的IP 和 port
	struct sockaddr_in saddr;					//struct sockaddr和struct sockaddr_in这两个结构体用来处理网络通信的地址
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(9999);				//转换成网络字节序，绑定端口
	saddr.sin_addr.s_addr = INADDR_ANY;			//实际值为0，0表示初始化为0:0:0:0 会自动读取绑定网卡ip地址
	int res = bind(fd, (struct sockaddr*)&saddr, sizeof(saddr));
	if (res == -1)
	{
		perror("bind");
		return -1;
	}

	//第三步：设置监听
	res = listen(fd, 128);
	if (res == -1)
	{
		perror("listen");
		return -1;
	}

	//创建线程池  参数：最少的线程数数量为3，最多的线程数量为8，任务队列最大100个任务
	ThreadPool* pool = thread_pool_create(3, 8, 100);
	PoolInfo* info = (PoolInfo*)malloc(sizeof(PoolInfo));
	info->p = pool;
	info->fd = fd;
	thread_pool_add_task(pool, accept_connect, info);

	// pthread_exit:退出当前线程，并不会影响其他线程
	pthread_exit(NULL);
	return 0;
}
void accept_connect(void* arg)
{
	PoolInfo* p_info = (PoolInfo*)arg;
	//第四步：阻塞并等待客户端连接
	int caddr_len = sizeof(struct sockaddr_in);
	while (1)
	{
		// 分配对应的信息结构体
		SockInfo*  s_info;
		s_info = (SockInfo*)malloc(sizeof(SockInfo));
		s_info->fd = accept(p_info->fd, (struct sockaddr*)&s_info->addr, &caddr_len);
		// 判断是否连接成功
		if (s_info->fd == -1)
		{
			perror("accept");
			break;
		}

		// 第五步：添加通讯的任务到任务队列
		thread_pool_add_task(p_info->p, working, s_info);
	}

	// 第六步：断开连接，关闭文件描述符
	close(p_info->fd);
}
void working(void* arg)
{
	SockInfo* s_info = (SockInfo*)arg;
	// 连接成功后，打印客户端的IP和端口信息	(传过来的IP和端口信息是网络字节序，需要转换为主机字节序，也就是小端)
	char ip[32];
	printf("客户端的ip：%s，端口：%d\n",
		inet_ntop(AF_INET, &s_info->addr.sin_addr.s_addr, ip, sizeof(ip)),
		ntohs(s_info->addr.sin_port));

	// 第五步：通讯		(我这里规定客户端献给服务端先发数据)
	while (1)
	{
		// 接受数据
		char buff[1024];
		int len = recv(s_info->fd, buff, sizeof(buff), 0);
		if (len > 0)
		{
			// 说明接收来自客户端的数据
			printf("client say: %s\n", buff);
			send(s_info->fd, buff, sizeof(buff), 0);
		}
		else if (len == 0)
		{
			printf("客户端断开连接。。。");
			break;
		}
		else
		{
			perror("recv");
			break;
		}
	}

	// 第六步：断开连接，关闭文件描述符
	close(s_info->fd);
}